/**********
This library is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the
Free Software Foundation; either version 3 of the License, or (at your
option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)

This library is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
more details.

You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
**********/
// "liveMedia"
// Copyright (c) 1996-2020 Live Networks, Inc.  All rights reserved.
// A RTSP server
// C++ header

#ifndef _RTSP_SERVER_HH
#define _RTSP_SERVER_HH

#ifndef _GENERIC_MEDIA_SERVER_HH
#include "GenericMediaServer.hh"
#endif
#ifndef _DIGEST_AUTHENTICATION_HH
#include "DigestAuthentication.hh"
#endif

class RTSPServer : public GenericMediaServer {
public:
	static RTSPServer* createNew(UsageEnvironment& env, Port ourPort = 554,
		UserAuthenticationDatabase* authDatabase = NULL,
		unsigned reclamationSeconds = 65);
	// If ourPort.num() == 0, we'll choose the port number
	// Note: The caller is responsible for reclaiming "authDatabase"
	// If "reclamationSeconds" > 0, then the "RTSPClientSession" state for
	//     each client will get reclaimed (and the corresponding RTP stream(s)
	//     torn down) if no RTSP commands - or RTCP "RR" packets - from the
	//     client are received in at least "reclamationSeconds" seconds.

	static Boolean lookupByName(UsageEnvironment& env, char const* name,
		RTSPServer*& resultServer);

	typedef void (responseHandlerForREGISTER)(RTSPServer* rtspServer, unsigned requestId, int resultCode, char* resultString);
	unsigned registerStream(ServerMediaSession* serverMediaSession,
		char const* remoteClientNameOrAddress, portNumBits remoteClientPortNum,
		responseHandlerForREGISTER* responseHandler,
		char const* username = NULL, char const* password = NULL,
		Boolean receiveOurStreamViaTCP = False,
		char const* proxyURLSuffix = NULL);
	// 'Register' the stream represented by "serverMediaSession" with the given remote client (specifed by name and port number).
	// This is done using our custom "REGISTER" RTSP command.
	// The function returns a unique number that can be used to identify the request; this number is also passed to "responseHandler".
	// When a response is received from the remote client (or the "REGISTER" request fails), the specified response handler
	//   (if non-NULL) is called.  (Note that the "resultString" passed to the handler was dynamically allocated,
	//   and should be delete[]d by the handler after use.)
	// If "receiveOurStreamViaTCP" is True, then we're requesting that the remote client access our stream using RTP/RTCP-over-TCP.
	//   (Otherwise, the remote client may choose regular RTP/RTCP-over-UDP streaming.)
	// "proxyURLSuffix" (optional) is used only when the remote client is also a proxy server.
	//   It tells the proxy server the suffix that it should use in its "rtsp://" URL (when front-end clients access the stream)

	typedef void (responseHandlerForDEREGISTER)(RTSPServer* rtspServer, unsigned requestId, int resultCode, char* resultString);
	unsigned deregisterStream(ServerMediaSession* serverMediaSession,
		char const* remoteClientNameOrAddress, portNumBits remoteClientPortNum,
		responseHandlerForDEREGISTER* responseHandler,
		char const* username = NULL, char const* password = NULL,
		char const* proxyURLSuffix = NULL);
	// Used to turn off a previous "registerStream()" - using our custom "DEREGISTER" RTSP command.

	char* rtspURL(ServerMediaSession const* serverMediaSession, int clientSocket = -1) const;
	// returns a "rtsp://" URL that could be used to access the
	// specified session (which must already have been added to
	// us using "addServerMediaSession()".
	// This string is dynamically allocated; caller should delete[]
	// (If "clientSocket" is non-negative, then it is used (by calling "getsockname()") to determine
	//  the IP address to be used in the URL.)
	char* rtspURLPrefix(int clientSocket = -1) const;
	// like "rtspURL()", except that it returns just the common prefix used by
	// each session's "rtsp://" URL.
	// This string is dynamically allocated; caller should delete[]

	UserAuthenticationDatabase* setAuthenticationDatabase(UserAuthenticationDatabase* newDB);
	// Changes the server's authentication database to "newDB", returning a pointer to the old database (if there was one).
	// "newDB" may be NULL (you can use this to disable authentication at runtime, if desired).

	void disableStreamingRTPOverTCP() {
		fAllowStreamingRTPOverTCP = False;
	}

	Boolean setUpTunnelingOverHTTP(Port httpPort);
	// (Attempts to) enable RTSP-over-HTTP tunneling on the specified port.
	// Returns True iff the specified port can be used in this way (i.e., it's not already being used for a separate HTTP server).
	// Note: RTSP-over-HTTP tunneling is described in
	//  http://mirror.informatimago.com/next/developer.apple.com/quicktime/icefloe/dispatch028.html
	//  and http://images.apple.com/br/quicktime/pdf/QTSS_Modules.pdf
	portNumBits httpServerPortNum() const; // in host byte order.  (Returns 0 if not present.)

protected:
	RTSPServer(UsageEnvironment& env,
		int ourSocket, Port ourPort,
		UserAuthenticationDatabase* authDatabase,
		unsigned reclamationSeconds);
	// called only by createNew();
	virtual ~RTSPServer();

	virtual char const* allowedCommandNames(); // used to implement "RTSPClientConnection::handleCmd_OPTIONS()"
	virtual Boolean weImplementREGISTER(char const* cmd/*"REGISTER" or "DEREGISTER"*/,
		char const* proxyURLSuffix, char*& responseStr);
	// used to implement "RTSPClientConnection::handleCmd_REGISTER()"
	// Note: "responseStr" is dynamically allocated (or NULL), and should be delete[]d after the call
	virtual void implementCmd_REGISTER(char const* cmd/*"REGISTER" or "DEREGISTER"*/,
		char const* url, char const* urlSuffix, int socketToRemoteServer,
		Boolean deliverViaTCP, char const* proxyURLSuffix);
	// used to implement "RTSPClientConnection::handleCmd_REGISTER()"

	virtual UserAuthenticationDatabase* getAuthenticationDatabaseForCommand(char const* cmdName);
	virtual Boolean specialClientAccessCheck(int clientSocket, struct sockaddr_in& clientAddr,
		char const* urlSuffix);
	// a hook that allows subclassed servers to do server-specific access checking
	// on each client (e.g., based on client IP address), without using digest authentication.
	virtual Boolean specialClientUserAccessCheck(int clientSocket, struct sockaddr_in& clientAddr,
		char const* urlSuffix, char const *username);
	// another hook that allows subclassed servers to do server-specific access checking
	// - this time after normal digest authentication has already taken place (and would otherwise allow access).
	// (This test can only be used to further restrict access, not to grant additional access.)

private: // redefined virtual functions
	virtual Boolean isRTSPServer() const;

public: // should be protected, but some old compilers complain otherwise
  // The state of a TCP connection used by a RTSP client:
	class RTSPClientSession; // forward
	class RTSPClientConnection : public GenericMediaServer::ClientConnection {
	public:
		// A data structure that's used to implement the "REGISTER" command:
		class ParamsForREGISTER {
		public:
			ParamsForREGISTER(char const* cmd/*"REGISTER" or "DEREGISTER"*/,
				RTSPClientConnection* ourConnection, char const* url, char const* urlSuffix,
				Boolean reuseConnection, Boolean deliverViaTCP, char const* proxyURLSuffix);
			virtual ~ParamsForREGISTER();
		private:
			friend class RTSPClientConnection;
			char const* fCmd;
			RTSPClientConnection* fOurConnection;
			char* fURL;
			char* fURLSuffix;
			Boolean fReuseConnection, fDeliverViaTCP;
			char* fProxyURLSuffix;
		};
	protected: // redefined virtual functions:
		virtual void handleRequestBytes(int newBytesRead);

	protected:
		RTSPClientConnection(RTSPServer& ourServer, int clientSocket, struct sockaddr_in clientAddr);
		virtual ~RTSPClientConnection();

		friend class RTSPServer;
		friend class RTSPClientSession;

		// Make the handler functions for each command virtual, to allow subclasses to reimplement them, if necessary:
		virtual void handleCmd_OPTIONS();
		// You probably won't need to subclass/reimplement this function; reimplement "RTSPServer::allowedCommandNames()" instead.
		virtual void handleCmd_GET_PARAMETER(char const* fullRequestStr); // when operating on the entire server
		virtual void handleCmd_SET_PARAMETER(char const* fullRequestStr); // when operating on the entire server
		virtual void handleCmd_DESCRIBE(char const* urlPreSuffix, char const* urlSuffix, char const* fullRequestStr);
		virtual void handleCmd_REGISTER(char const* cmd/*"REGISTER" or "DEREGISTER"*/,
			char const* url, char const* urlSuffix, char const* fullRequestStr,
			Boolean reuseConnection, Boolean deliverViaTCP, char const* proxyURLSuffix);
		// You probably won't need to subclass/reimplement this function;
		//     reimplement "RTSPServer::weImplementREGISTER()" and "RTSPServer::implementCmd_REGISTER()" instead.
		virtual void handleCmd_bad();
		virtual void handleCmd_notSupported();
		virtual void handleCmd_notFound();
		virtual void handleCmd_sessionNotFound();
		virtual void handleCmd_unsupportedTransport();
		// Support for optional RTSP-over-HTTP tunneling:
		virtual Boolean parseHTTPRequestString(char* resultCmdName, unsigned resultCmdNameMaxSize,
			char* urlSuffix, unsigned urlSuffixMaxSize,
			char* sessionCookie, unsigned sessionCookieMaxSize,
			char* acceptStr, unsigned acceptStrMaxSize);
		virtual void handleHTTPCmd_notSupported();
		virtual void handleHTTPCmd_notFound();
		virtual void handleHTTPCmd_OPTIONS();
		virtual void handleHTTPCmd_TunnelingGET(char const* sessionCookie);
		virtual Boolean handleHTTPCmd_TunnelingPOST(char const* sessionCookie, unsigned char const* extraData, unsigned extraDataSize);
		virtual void handleHTTPCmd_StreamingGET(char const* urlSuffix, char const* fullRequestStr);
	protected:
		void resetRequestBuffer();
		void closeSocketsRTSP();
		static void handleAlternativeRequestByte(void*, u_int8_t requestByte);
		void handleAlternativeRequestByte1(u_int8_t requestByte);
		Boolean authenticationOK(char const* cmdName, char const* urlSuffix, char const* fullRequestStr);
		void changeClientInputSocket(int newSocketNum, unsigned char const* extraData, unsigned extraDataSize);
		// used to implement RTSP-over-HTTP tunneling
		static void continueHandlingREGISTER(ParamsForREGISTER* params);
		virtual void continueHandlingREGISTER1(ParamsForREGISTER* params);

		// Shortcuts for setting up a RTSP response (prior to sending it):
		void setRTSPResponse(char const* responseStr);
		void setRTSPResponse(char const* responseStr, u_int32_t sessionId);
		void setRTSPResponse(char const* responseStr, char const* contentStr);
		void setRTSPResponse(char const* responseStr, u_int32_t sessionId, char const* contentStr);

		RTSPServer& fOurRTSPServer; // same as ::fOurServer
		int& fClientInputSocket; // aliased to ::fOurSocket
		int fClientOutputSocket;
		Boolean fIsActive;
		unsigned char* fLastCRLF;
		unsigned fRecursionCount;
		char const* fCurrentCSeq;
		Authenticator fCurrentAuthenticator; // used if access control is needed
		char* fOurSessionCookie; // used for optional RTSP-over-HTTP tunneling
		unsigned fBase64RemainderCount; // used for optional RTSP-over-HTTP tunneling (possible values: 0,1,2,3)
	};

	// The state of an individual client session (using one or more sequential TCP connections) handled by a RTSP server:
	class RTSPClientSession : public GenericMediaServer::ClientSession {
	protected:
		RTSPClientSession(RTSPServer& ourServer, u_int32_t sessionId);
		virtual ~RTSPClientSession();

		friend class RTSPServer;
		friend class RTSPClientConnection;
		// Make the handler functions for each command virtual, to allow subclasses to redefine them:
		virtual void handleCmd_SETUP(RTSPClientConnection* ourClientConnection,
			char const* urlPreSuffix, char const* urlSuffix, char const* fullRequestStr);
		virtual void handleCmd_withinSession(RTSPClientConnection* ourClientConnection,
			char const* cmdName,
			char const* urlPreSuffix, char const* urlSuffix,
			char const* fullRequestStr);
		virtual void handleCmd_TEARDOWN(RTSPClientConnection* ourClientConnection,
			ServerMediaSubsession* subsession);
		virtual void handleCmd_PLAY(RTSPClientConnection* ourClientConnection,
			ServerMediaSubsession* subsession, char const* fullRequestStr);
		virtual void handleCmd_PAUSE(RTSPClientConnection* ourClientConnection,
			ServerMediaSubsession* subsession);
		virtual void handleCmd_GET_PARAMETER(RTSPClientConnection* ourClientConnection,
			ServerMediaSubsession* subsession, char const* fullRequestStr);
		virtual void handleCmd_SET_PARAMETER(RTSPClientConnection* ourClientConnection,
			ServerMediaSubsession* subsession, char const* fullRequestStr);
	protected:
		void deleteStreamByTrack(unsigned trackNum);
		void reclaimStreamStates();
		Boolean isMulticast() const { return fIsMulticast; }

		// Shortcuts for setting up a RTSP response (prior to sending it):
		void setRTSPResponse(RTSPClientConnection* ourClientConnection, char const* responseStr) { ourClientConnection->setRTSPResponse(responseStr); }
		void setRTSPResponse(RTSPClientConnection* ourClientConnection, char const* responseStr, u_int32_t sessionId) { ourClientConnection->setRTSPResponse(responseStr, sessionId); }
		void setRTSPResponse(RTSPClientConnection* ourClientConnection, char const* responseStr, char const* contentStr) { ourClientConnection->setRTSPResponse(responseStr, contentStr); }
		void setRTSPResponse(RTSPClientConnection* ourClientConnection, char const* responseStr, u_int32_t sessionId, char const* contentStr) { ourClientConnection->setRTSPResponse(responseStr, sessionId, contentStr); }

	protected:
		RTSPServer& fOurRTSPServer; // same as ::fOurServer
		Boolean fIsMulticast, fStreamAfterSETUP;
		unsigned char fTCPStreamIdCount; // used for (optional) RTP/TCP
		Boolean usesTCPTransport() const { return fTCPStreamIdCount > 0; }
		unsigned fNumStreamStates;
		struct streamState {
			ServerMediaSubsession* subsession;
			int tcpSocketNum;
			void* streamToken;
		} *fStreamStates;
	};

protected: // redefined virtual functions
  // If you subclass "RTSPClientConnection", then you must also redefine this virtual function in order
  // to create new objects of your subclass:
	virtual ClientConnection* createNewClientConnection(int clientSocket, struct sockaddr_in clientAddr);

protected:
	// If you subclass "RTSPClientSession", then you must also redefine this virtual function in order
	// to create new objects of your subclass:
	virtual ClientSession* createNewClientSession(u_int32_t sessionId);

private:
	static void incomingConnectionHandlerHTTP(void*, int /*mask*/);
	void incomingConnectionHandlerHTTP();

	void noteTCPStreamingOnSocket(int socketNum, RTSPClientSession* clientSession, unsigned trackNum);
	void unnoteTCPStreamingOnSocket(int socketNum, RTSPClientSession* clientSession, unsigned trackNum);
	void stopTCPStreamingOnSocket(int socketNum);

private:
	friend class RTSPClientConnection;
	friend class RTSPClientSession;
	friend class RegisterRequestRecord;
	friend class DeregisterRequestRecord;
	int fHTTPServerSocket; // for optional RTSP-over-HTTP tunneling
	Port fHTTPServerPort; // ditto
	HashTable* fClientConnectionsForHTTPTunneling; // maps client-supplied 'session cookie' strings to "RTSPClientConnection"s
	  // (used only for optional RTSP-over-HTTP tunneling)
	HashTable* fTCPStreamingDatabase;
	// maps TCP socket numbers to ids of sessions that are streaming over it (RTP/RTCP-over-TCP)
	HashTable* fPendingRegisterOrDeregisterRequests;
	unsigned fRegisterOrDeregisterRequestCounter;
	UserAuthenticationDatabase* fAuthDB;
	Boolean fAllowStreamingRTPOverTCP; // by default, True
};


////////// A subclass of "RTSPServer" that implements the "REGISTER" command to set up proxying on the specified URL //////////

class RTSPServerWithREGISTERProxying : public RTSPServer {
public:
	static RTSPServerWithREGISTERProxying* createNew(UsageEnvironment& env, Port ourPort = 554,
		UserAuthenticationDatabase* authDatabase = NULL,
		UserAuthenticationDatabase* authDatabaseForREGISTER = NULL,
		unsigned reclamationSeconds = 65,
		Boolean streamRTPOverTCP = False,
		int verbosityLevelForProxying = 0,
		char const* backEndUsername = NULL,
		char const* backEndPassword = NULL);

protected:
	RTSPServerWithREGISTERProxying(UsageEnvironment& env, int ourSocket, Port ourPort,
		UserAuthenticationDatabase* authDatabase, UserAuthenticationDatabase* authDatabaseForREGISTER,
		unsigned reclamationSeconds,
		Boolean streamRTPOverTCP, int verbosityLevelForProxying,
		char const* backEndUsername, char const* backEndPassword);
	// called only by createNew();
	virtual ~RTSPServerWithREGISTERProxying();

protected: // redefined virtual functions
	virtual char const* allowedCommandNames();
	virtual Boolean weImplementREGISTER(char const* cmd/*"REGISTER" or "DEREGISTER"*/,
		char const* proxyURLSuffix, char*& responseStr);
	virtual void implementCmd_REGISTER(char const* cmd/*"REGISTER" or "DEREGISTER"*/,
		char const* url, char const* urlSuffix, int socketToRemoteServer,
		Boolean deliverViaTCP, char const* proxyURLSuffix);
	virtual UserAuthenticationDatabase* getAuthenticationDatabaseForCommand(char const* cmdName);

private:
	Boolean fStreamRTPOverTCP;
	int fVerbosityLevelForProxying;
	unsigned fRegisteredProxyCounter;
	char* fAllowedCommandNames;
	UserAuthenticationDatabase* fAuthDBForREGISTER;
	char* fBackEndUsername;
	char* fBackEndPassword;
};


// A special version of "parseTransportHeader()", used just for parsing the "Transport:" header
// in an incoming "REGISTER" command:
void parseTransportHeaderForREGISTER(char const* buf, // in
	Boolean &reuseConnection, // out
	Boolean& deliverViaTCP, // out
	char*& proxyURLSuffix); // out

#endif
